define(["require", "exports", "./utils"], function (require, exports, utils_1) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    class FlowDesigner {
        flowProperties = {};
        outputConditions = new Map();
        nodeProperties = new Array();
        win = null;
        canvas;
        nodes = [];
        draggingNode = null;
        point = { x: 0, y: 0 };
        offset = { left: 0, top: 0 };
        lineOffsets = new Map();
        jointedLine = null;
        hasStartNode = false;
        hasEndNode = false;
        draggable = true;
        editable = true;
        followPorts = new Map();
        jointingPorts = new Array();
        nodePropDialog;
        constructor(editable = true, draggable) {
            this.editable = editable;
            this.draggable = typeof (draggable) == "undefined" || draggable;
            this.canvas = document.querySelector('.canvas');
            this.nodePropDialog = document.querySelector("#nodePropDialog");
            this.initToolbar();
            this.initCanvas();
        }
        initToolbar() {
            const toolbar = document.querySelector('.toolbar');
            toolbar.addEventListener('click', (e) => {
                if (e.target instanceof HTMLButtonElement) {
                    const btn = e.target;
                    this.createNode(btn.dataset.type);
                }
            });
        }
        initCanvas() {
            window.onbeforeunload = () => {
                if (this.win && !this.win.closed) {
                    this.win.close();
                }
            };
            document.addEventListener('contextmenu', (event) => {
                event.preventDefault();
            });
            this.makeDraggable(this.canvas);
            this.canvas.addEventListener('mousedown', this.handleMouseDown.bind(this));
            this.canvas.addEventListener('mousemove', this.handleMouseMove.bind(this));
            this.canvas.addEventListener('mouseup', this.handleMouseUp.bind(this));
        }
        createNodeLabel(type) {
            const label = document.createElement('input');
            label.id = (0, utils_1.generateId)('label');
            label.type = 'text';
            label.classList.add('node-label');
            label.value = type === 'start' ? '流程发起' :
                type === 'end' ? '流程结束' :
                    type === 'branch' ? '分支' :
                        type === 'merge' ? '聚合' :
                            type === 'line' ? '连接线' : '处理节点';
            if (!this.editable) {
                label.disabled = true;
            }
            return label;
        }
        createNode(type) {
            if (type === 'start' && this.hasStartNode) {
                alert('只能创建一个开始节点');
                return;
            }
            if (type === 'end' && this.hasEndNode) {
                alert('只能创建一个结束节点');
                return;
            }
            const node = document.createElement('div');
            node.id = (0, utils_1.generateId)(type);
            node.dataset.flag = (0, utils_1.generateFlag)(type);
            if (type == "line") {
                node.classList.add('line');
            }
            else {
                node.classList.add('node');
            }
            const label = this.createNodeLabel(type);
            label.dataset.flag = (0, utils_1.generateFlag)(type);
            this.makeDraggable(label);
            switch (type) {
                case 'start':
                    node.classList.add('start-node');
                    node.innerText = "开始";
                    this.hasStartNode = true;
                    break;
                case 'end':
                    node.classList.add('end-node');
                    node.innerHTML = "结束";
                    this.hasEndNode = true;
                    break;
                case 'branch':
                    node.classList.add('branch-node');
                    node.innerHTML = "◆";
                    break;
                case 'merge':
                    node.classList.add('merge-node');
                    node.innerHTML = "▶";
                    break;
                case "normal":
                    node.innerHTML = "节点";
                    break;
                case "line":
                    node.innerHTML = `<div class="arrow">▶</div>`;
                    break;
            }
            node.style.left = `20px`;
            node.style.top = `20px`;
            this.canvas.appendChild(node);
            this.nodes.push(node);
            node.appendChild(label);
            this.addPorts(node, type);
            this.makeDraggable(node);
        }
        addPort(node, portType) {
            const port = document.createElement('div');
            port.classList.add('port', portType);
            port.id = (0, utils_1.generateId)(portType);
            port.dataset.flag = (0, utils_1.generateFlag)(portType);
            port.dataset.owner = port.id;
            if (node.classList.contains('branch-node')) {
                port.style.right = '-6px';
                port.style.top = `${-6 + node.querySelectorAll('.out-port').length * 12}px`;
            }
            else if (node.classList.contains('merge-node')) {
                port.style.left = `${-6 + node.querySelectorAll('.in-port').length * 12}px`;
                port.style.top = '-6px';
            }
            node.appendChild(port);
            this.makeDraggable(port);
            this.addPortAndPos(port);
        }
        addPorts(node, type) {
            if (type != 'start') {
                const inPort = document.createElement('div');
                inPort.id = (0, utils_1.generateId)('inport');
                inPort.dataset.owner = node.id;
                inPort.classList.add('port', 'in-port');
                inPort.dataset.flag = (0, utils_1.generateFlag)("in-port");
                if (type == "line") {
                    this.canvas.appendChild(inPort);
                    inPort.style.left = `${node.offsetLeft - 5}px`;
                    inPort.style.top = `${node.offsetTop - node.offsetHeight}px`;
                    inPort.style.zIndex = "10";
                    this.makeDraggable(inPort);
                }
                else {
                    this.canvas.appendChild(inPort);
                    inPort.style.left = `${node.offsetLeft - 12}px`;
                    inPort.style.top = `${node.offsetTop + node.offsetWidth / 2 - 5}px`;
                    this.makeDraggable(inPort);
                    this.addPortAndPos(inPort);
                }
            }
            if (type != 'end') {
                const outPort = document.createElement('div');
                outPort.id = (0, utils_1.generateId)('outport');
                outPort.dataset.owner = node.id;
                outPort.classList.add('port', 'out-port');
                outPort.dataset.flag = (0, utils_1.generateFlag)("out-port");
                if (type == "line") {
                    this.canvas.appendChild(outPort);
                    outPort.style.left = `${node.offsetLeft + node.offsetWidth + 6}px`;
                    outPort.style.top = `${node.offsetTop + node.offsetHeight / 2 - 5}px`;
                    outPort.style.zIndex = "10";
                    this.makeDraggable(outPort);
                }
                else {
                    this.canvas.appendChild(outPort);
                    outPort.style.left = `${node.offsetLeft + node.offsetWidth + 3}px`;
                    outPort.style.top = `${node.offsetTop + node.offsetWidth / 2 - 3}px`;
                    this.makeDraggable(outPort);
                    this.addPortAndPos(outPort);
                }
            }
            if (type == 'branch') {
                const outPort2 = document.createElement('div');
                outPort2.id = (0, utils_1.generateId)('outport');
                outPort2.dataset.owner = node.id;
                outPort2.dataset.flag = (0, utils_1.generateFlag)("out-port");
                outPort2.classList.add('port', 'out-port');
                this.canvas.appendChild(outPort2);
                outPort2.style.left = `${node.offsetLeft + node.offsetWidth - 6}px`;
                outPort2.style.top = `${node.offsetTop + node.offsetWidth / 2 + 24}px`;
                this.makeDraggable(outPort2);
                this.addPortAndPos(outPort2);
            }
            if (type == 'merge') {
                const inPort2 = document.createElement('div');
                inPort2.classList.add('port', 'in-port');
                inPort2.id = (0, utils_1.generateId)('inport');
                inPort2.dataset.owner = node.id;
                inPort2.dataset.flag = (0, utils_1.generateFlag)("in-port");
                this.canvas.appendChild(inPort2);
                inPort2.style.left = `${node.offsetLeft - 10}px`;
                inPort2.style.top = `${node.offsetTop + node.offsetWidth / 2 + 16}px`;
                this.makeDraggable(inPort2);
                this.addPortAndPos(inPort2);
            }
        }
        showContextMenu(node, x, y, flowProp = false) {
            if (!this.editable) {
                return;
            }
            if (node.tagName.toLowerCase() == "input") {
                return;
            }
            let branchOutport = null;
            if (node.classList.contains("port")) {
                const port = node;
                const owner = this.canvas.querySelector("#" + port.dataset.owner);
                if (owner.classList.contains("line") && port.classList.contains("out-port")) {
                    return;
                }
                if (owner.classList.contains("line") && port.classList.contains("in-port")) {
                    const outport = this.canvas.querySelector("#" + port.dataset.jointed);
                    if (outport && outport.dataset.owner) {
                        const id = outport.dataset.owner;
                        if (id && id.includes("branch")) {
                            branchOutport = outport;
                        }
                    }
                    if (!branchOutport) {
                        return;
                    }
                }
                const el = this.canvas.querySelector("#" + port.dataset.owner);
                if (el && el.classList.contains("node")) {
                    if (el.classList.contains("start-node") || el.classList.contains("end-node")) {
                        return;
                    }
                    if (el.classList.contains("branch-node") && port.classList.contains("in-port")) {
                        return;
                    }
                    if (el.classList.contains("merge-node") && port.classList.contains("out-port")) {
                        return;
                    }
                }
            }
            const existingMenu = document.querySelector('.node-context-menu');
            if (existingMenu) {
                existingMenu.remove();
            }
            const menu = document.createElement('div');
            menu.classList.add('node-context-menu');
            menu.style.position = 'fixed';
            menu.style.left = `${x}px`;
            menu.style.top = `${y}px`;
            if (flowProp) {
                const validateFlowItem = document.createElement('div');
                validateFlowItem.classList.add('menu-item');
                validateFlowItem.textContent = '验证设计';
                validateFlowItem.addEventListener('click', () => {
                    if (this.validate()) {
                        alert('设计验证通过');
                    }
                    else {
                        alert('设计存在错误，请检查');
                    }
                    menu.remove();
                });
                const saveFlowItem = document.createElement('div');
                saveFlowItem.classList.add('menu-item');
                saveFlowItem.textContent = '保存设计';
                saveFlowItem.addEventListener('click', () => {
                    this.save();
                    alert('设计已保存');
                    menu.remove();
                });
                const editFlowItem = document.createElement('div');
                editFlowItem.classList.add('menu-item');
                editFlowItem.textContent = '流程属性';
                editFlowItem.addEventListener('click', () => {
                    menu.remove();
                });
                menu.appendChild(editFlowItem);
                menu.appendChild(validateFlowItem);
                menu.appendChild(saveFlowItem);
            }
            else {
                const editItem = document.createElement('div');
                editItem.classList.add('menu-item');
                editItem.textContent = '属性编辑';
                editItem.addEventListener('click', () => {
                    this.editNodeProperties((!!branchOutport) ? branchOutport : node);
                    menu.remove();
                });
                if (!node.classList.contains("line")
                    && !node.classList.contains("branch-node")
                    && !node.classList.contains("merge-node")
                    && !node.classList.contains("in-port")
                    && !(node.dataset.owner?.startsWith("normal"))) {
                    menu.appendChild(editItem);
                }
                if (node.classList.contains("branch-node")) {
                    const addOutPortItem = document.createElement('div');
                    addOutPortItem.classList.add('menu-item');
                    addOutPortItem.textContent = '添加出口';
                    addOutPortItem.addEventListener('click', () => {
                        this.addPort(node, 'out-port');
                        menu.remove();
                    });
                    menu.appendChild(addOutPortItem);
                }
                if (node.classList.contains("merge-node")) {
                    const addInPortItem = document.createElement('div');
                    addInPortItem.classList.add('menu-item');
                    addInPortItem.textContent = '添加入口';
                    addInPortItem.addEventListener('click', () => {
                        this.addPort(node, 'in-port');
                        menu.remove();
                    });
                    menu.appendChild(addInPortItem);
                }
                const deleteItem = document.createElement('div');
                deleteItem.classList.add('menu-item');
                deleteItem.textContent = '删除';
                deleteItem.addEventListener('click', () => {
                    this.deleteNode(node);
                    menu.remove();
                });
                if (!branchOutport && !node.dataset.owner?.startsWith("normal")) {
                    menu.appendChild(deleteItem);
                }
            }
            if (menu.innerHTML != "") {
                document.body.appendChild(menu);
                document.addEventListener('click', () => menu.remove(), { once: true });
            }
        }
        editNodeProperties(node) {
            const flag = node.dataset.flag;
            let conditon = (0, utils_1.getStorageValue)(flag);
            let url = "";
            if (node.classList.contains("out-port")) {
                conditon = conditon || this.outputConditions.get(flag);
                url = "view/outport.html";
            }
            else if (node.classList.contains("node")
                || node.classList.contains("start-node")
                || node.classList.contains("end-node")) {
                if (!conditon) {
                    conditon = this.nodeProperties.find(nodeProp => { nodeProp.nodeFlag == flag; });
                }
                url = "view/node.html";
            }
            conditon = conditon ?? {};
            utils_1.storage.setItem(flag, JSON.stringify(conditon));
            const qs = (0, utils_1.queryString)({ flag });
            (0, utils_1.showDialog)(url + "?" + qs, this.nodePropDialog);
        }
        makeDraggable(node) {
            node.addEventListener('mousedown', (evt) => {
                if (evt.target == node) {
                    if (evt.button === 0 && this.draggable) {
                        this.draggingNode = node;
                        this.offset.left = node.offsetLeft;
                        this.offset.top = node.offsetTop;
                        this.point.x = evt.pageX;
                        this.point.y = evt.pageY;
                        this.canvas.querySelectorAll(".line").forEach(line => {
                            this.lineOffsets.set(line, { top: line.offsetTop, left: line.offsetLeft });
                        });
                        if (node.classList.contains("port")) {
                            const line = this.canvas.querySelector("#" + node.dataset.owner);
                            if (line && line.classList.contains("line")) {
                                this.jointedLine = line;
                                const arr = Array.from(this.followPorts.keys())
                                    .filter(p => p.dataset.owner && !p.dataset.owner.startsWith("line"));
                                if (node.classList.contains("in-port")) {
                                    this.jointingPorts = arr.filter(p => p.classList.contains("out-port"));
                                }
                                else {
                                    this.jointingPorts = arr.filter(p => p.classList.contains("in-port"));
                                }
                            }
                        }
                        else {
                            this.canvas.querySelectorAll(`[data-owner]`).forEach(p => {
                                this.addPortAndPos(p);
                            });
                        }
                    }
                    else if (evt.button === 2) {
                        evt.preventDefault();
                        this.showContextMenu(node, evt.clientX, evt.clientY, node.classList.contains("canvas"));
                    }
                }
            });
        }
        deleteNode(node) {
            this.nodes = this.nodes.filter(n => n !== node);
            if (node.classList.contains("start-node")) {
                this.hasStartNode = false;
            }
            else if (node.classList.contains("end-node")) {
                this.hasEndNode = false;
            }
            if (node.classList.contains("port")) {
                const el = this.canvas.querySelector("#" + node.dataset.owner);
                if (el && el.classList.contains("node")) {
                    if (el.classList.contains("branch-node")) {
                        const len = el.querySelectorAll(".out-port").length;
                        if (len <= 2) {
                            alert("至少保留2个出口");
                            return;
                        }
                    }
                    if (el.classList.contains("merge-node")) {
                        const len = el.querySelectorAll(".in-port").length;
                        if (len <= 2) {
                            alert("至少保留2个入口");
                            return;
                        }
                    }
                }
            }
            this.canvas.querySelectorAll(`[data-owner="${node.id}"]`).forEach(el => {
                this.outputConditions.delete(el.id);
                el.remove();
            });
            node.remove();
        }
        handleMouseDown(evt) {
        }
        handleMouseMove(evt) {
            if (this.draggingNode) {
                if (this.draggingNode.classList.contains("canvas")) {
                    return;
                }
                const dx = evt.pageX - this.point.x;
                const dy = evt.pageY - this.point.y;
                let x = this.offset.left + dx;
                let y = this.offset.top + dy;
                this.draggingNode.style.left = `${x}px`;
                this.draggingNode.style.top = `${y}px`;
                if (this.draggingNode.classList.contains("port")) {
                    this.drawLine(this.draggingNode, this.jointedLine, dx, dy);
                }
                else {
                    this.followPorts.forEach((pos, port) => {
                        if (port.dataset.owner == this.draggingNode.id) {
                            port.style.left = `${pos.x + dx}px`;
                            port.style.top = `${pos.y + dy}px`;
                            const jp = this.getJointedPort(port);
                            if (jp && this.followPorts.has(jp)) {
                                const jpPos = this.followPorts.get(jp);
                                jp.style.left = `${jpPos.x + dx}px`;
                                jp.style.top = `${jpPos.y + dy}px`;
                                const tempLine = document.querySelector("#" + jp.dataset.owner);
                                this.drawLine(jp, tempLine, dx, dy);
                            }
                        }
                    });
                    if (this.draggingNode.classList.contains("line")) {
                        const d = dx * dx + dy * dy;
                        if (d > 80) {
                            this.canvas.querySelectorAll(`[data-owner='${this.draggingNode.id}']`).forEach(port => {
                                const jointedId = port.dataset.jointed;
                                port.style.backgroundColor = "#FFF";
                                delete port.dataset.jointed;
                                const tmp = document.querySelector("#" + jointedId);
                                if (tmp) {
                                    tmp.style.backgroundColor = "red";
                                }
                            });
                        }
                    }
                }
            }
        }
        handleMouseUp() {
            this.draggingNode = null;
            this.jointedLine = null;
        }
        drawLine(port, line, dx, dy) {
            if (!line) {
                return;
            }
            let anotherPort;
            document.querySelectorAll(`[data-owner='${line.id}']`).forEach(p => {
                if (p.id != port.id) {
                    anotherPort = p;
                    return false;
                }
            });
            const x = port.offsetLeft - anotherPort.offsetLeft;
            const y = port.offsetTop - anotherPort.offsetTop;
            const len = Math.pow(x * x + y * y, 0.5);
            line.style.width = `${len - 6}px`;
            let angle = (0, utils_1.getPortsRotation)(port, anotherPort);
            if (port.id.startsWith("inport")) {
                const offset = this.lineOffsets.get(line);
                line.style.left = offset.left + dx + "px";
                line.style.top = offset.top + dy + "px";
            }
            line.style.transform = `rotate(${angle}deg)`;
            this.jointPoint(port);
            line.querySelector("input").style.transform = `rotate(${360 - angle}deg) translateX(-50%)`;
        }
        jointPoint(port) {
            this.jointingPorts.forEach(p => {
                const len = (0, utils_1.calcDistance)(port, p);
                if (len <= 8) {
                    if (this.canvas.querySelector(`[data-jointed='${p.id}']`)) {
                        return;
                    }
                    port.style.backgroundColor = "green";
                    port.dataset.jointed = p.id;
                    if (this.jointedLine && port.classList.contains("in-port")) {
                        this.jointedLine.style.left = `${port.offsetLeft + port.offsetWidth / 2}px`;
                        this.jointedLine.style.top = `${port.offsetTop + 2}px`;
                    }
                    p.style.backgroundColor = "#fff";
                }
                else {
                    const id = port.dataset.jointed;
                    if (id == p.id) {
                        p.style.backgroundColor = "red";
                        port.style.backgroundColor = "#fff";
                        delete port.dataset.jointed;
                    }
                }
            });
        }
        addPortAndPos(el) {
            const e = el;
            this.followPorts.set(e, { x: e.offsetLeft, y: e.offsetTop });
        }
        getJointedPort(port) {
            return document.querySelector(`[data-jointed='${port.id}']`);
        }
        validate() {
            let len = document.querySelectorAll(".start-node").length;
            if (len == 0) {
                alert("没有开始节点");
                return false;
            }
            if (len != 1) {
                alert("只能有一个开始节点");
                return false;
            }
            len = document.querySelectorAll(".start-node").length;
            if (len == 0) {
                alert("没有结束节点");
                return false;
            }
            if (len != 1) {
                alert("只能有一个结束节点");
                return false;
            }
            let r = this.followPorts.keys()
                .filter(port => !port.dataset.jointed)
                .every(port => !!document.querySelector(`[data-jointed="${port.id}"]`));
            if (!r) {
                alert("有节点未连接");
                return false;
            }
            return true;
        }
        save() {
            if (!this.validate()) {
                this.flowProperties.disabled = false;
                alert("验证未通过，此设计将设置为禁用");
            }
            this.flowProperties.content = this.canvas.innerHTML;
            Array.from(this.outputConditions.keys())
                .filter(id => !this.canvas.querySelector("#" + id))
                .forEach(id => {
                this.outputConditions.delete(id);
            });
            this.canvas.querySelectorAll(`.out-port`).forEach(outport => {
                const flag = outport.dataset.flag;
                if (flag?.startsWith("out-port")) {
                    const value = (0, utils_1.getStorageValue)(flag);
                    if (value) {
                        value.nodeId = outport.id;
                        this.outputConditions.set(value.nodeId, value);
                    }
                }
            });
            const data = {
                flowProperties: this.flowProperties,
                outputConditions: this.outputConditions,
                nodeProperties: this.nodeProperties
            };
            const url = "flow-save?" + (0, utils_1.queryString)((0, utils_1.getUrlParams)());
            utils_1.http.post(url, data).then(res => {
            });
        }
        load() {
            const params = (0, utils_1.getUrlParams)();
            const qs = (0, utils_1.queryString)({ tid: params["tid"] || "", fid: params["fid"] || "" });
            const url = "?" + qs;
        }
    }
    exports.default = FlowDesigner;
});
